/*
 * Monitors for new files in repository folder
 */
package do_.com.pbd.business.referrals.creditcardreferralprogramstatusupdater.impl;

import do_.com.bpd.business.referrals.impl.CaseExtractor;
import do_.com.bpd.business.referrals.persistence.controllers.BusinessReferralCaseJpaController;
import do_.com.bpd.business.referrals.persistence.entities.BusinessReferralCase;
import do_.com.bpd.business.referrals.persistence.entities.Referral;
import do_.com.pbd.business.referrals.creditcardreferralprogramstatusupdater.Installer;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Developer
 */
public class NewFilesMonitor {

    PropertyChangeSupport pcs = null;
    private static final Logger logger = Logger.getLogger(NewFilesMonitor.class.getName());
    public static final int NON_RUNNING_INTERVAL = 5000;
//    public static int POLLING_INTERVAL = 0;
    public static int MINIMUM_POLLING_INTERVAL = 30000;
//    public static boolean RUNNING=false;
    public static final String NEWFILESMONITORBUSY = "NewFilesMonitorBusy";
    public static String FILESMONITORKEEPMONITORING="FILESMONITORKEEPMONITORING";
    private boolean keepmonitoring=true;

    public NewFilesMonitor() {

        pcs = new PropertyChangeSupport(this);

        Runnable runnable = new Runnable() {

            private int counter = 0;
            private boolean inTestMode = false;

            @Override
            public void run() {
                System.out.println("RUNNING THREAD.");
                System.setProperty(NEWFILESMONITORBUSY, "0");
                String status = System.getProperty(FILESMONITORKEEPMONITORING);
                if ((status != null) && (status.equalsIgnoreCase("RUNNING"))) {
                    System.setProperty(FILESMONITORKEEPMONITORING, "STOP");
                    try {
                        Thread.sleep(20000);// Waiting to finish another Thread
                        System.out.println("Finish the stop.");
                    } catch (InterruptedException ex) {
                        System.out.println("Error stoping the before Thread.");
                        Logger.getLogger(NewFilesMonitor.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
                System.setProperty(FILESMONITORKEEPMONITORING, "RUNNING");
                while (keepmonitoring) {
                    String statusX = System.getProperty(FILESMONITORKEEPMONITORING);

                    if ((statusX != null) && (statusX.equalsIgnoreCase("STOP"))) {
                        System.out.println("Stoping monitor Thread.");
                        break;
                    } else {
//                        logger.log(Level.INFO, "------------------");
                    }
                    if (!Installer.RUN_STATUS.equals("1")) {
                        logger.log(Level.INFO, "Monitoring stopped.");
                        try {
                            Thread.sleep(NON_RUNNING_INTERVAL);
                            continue;
                        } catch (InterruptedException ex) {
                            logger.log(Level.INFO, "ERROR FATAL:{0}", ex.getMessage());
                            Logger.getLogger(NewFilesMonitor.class.getName()).log(Level.SEVERE, ex.getMessage(), ex);
                        }
                    }
                    try {
                        if ("0".equals(System.getProperty(NEWFILESMONITORBUSY))) {
                            logger.log(Level.INFO, "NewFileMonitor is not busy.  Starting job");
                            System.setProperty(NEWFILESMONITORBUSY, "1");
                            monitorForNewFiles();
                        } else {
                            logger.log(Level.INFO, "NewFileMonitor is busy. Skiping!");
                        }
                    } catch (IOException ex) {
                        System.out.println("<IOException>:" + ex.getMessage());
                        System.setProperty(NEWFILESMONITORBUSY, "0");
                        Logger.getLogger(NewFilesMonitor.class.getName()).log(Level.SEVERE, ex.getMessage(), ex);
                    }
                    counter++;
                    if (counter > 20 && inTestMode) {
                        logger.log(Level.INFO, "Monitoring stopped");
                        break;
                    }
                    try {
                        Thread.sleep(Installer.CURRENT_POLLING_RATE);
                    } catch (InterruptedException ex) {
                        Logger.getLogger(NewFilesMonitor.class.getName()).log(Level.SEVERE, ex.getMessage(), ex);
                    }
                }
                System.out.println("Leave the Thread.");
            }

            private void monitorForNewFiles() throws IOException {

                //Find new files and save the events found
                NewFilesFinder nff = new NewFilesFinder();
                List<File> filesFound = nff.findFiles();

                if (filesFound.isEmpty()) {
                    System.setProperty(NEWFILESMONITORBUSY, "0");
                    logger.log(Level.INFO, "No new cases files found. Skipping process");
                    return;
                }
                try {
                    //Delete old cases
                    deleteOldCases();
                    //ExtractCases
                    moveCasesToDatabase(filesFound);
                    //Backup files
                    nff.backupFile(filesFound);
                    //Find last events entered
                    //Associate with referral
                    updateCasesAndReferrals();
                    //Just before deleting the cases 
                    processUpdatedCases();
                    //Delete old cases
                    deleteOldCases();
                } finally {
                    System.setProperty(NEWFILESMONITORBUSY, "0");
                }
            }

            private boolean pass(String status) {
                if (status != null) {
                    //AGREGAR AQUI TODOS LOS ESTADOS QUE VENGAN DE CC
                    if (status.equalsIgnoreCase("APPROVED") || status.equalsIgnoreCase("CAPTURED") || status.equalsIgnoreCase("CANCELLED")|| status.equalsIgnoreCase("UNCONFIR")) {
                        return true;
                    }
                }
                return false;
            }

            private boolean isEntyProductNumber(String texto, String status) {
                /*
                public static final String NEW_STATUS = "NEW";
                public static final String DELETED_STATUS = "DELETED";
                public static final String CAPTURED_STATUS = "CAPTURED";//CA
                public static final String ISSUED_STATUS = "ISSUED";//EM
                public static final String ACTIVATED_STATUS = "ACTIVATED";//AC
                public static final String APPROVED_STATUS = "APPROVED";//AP
                public static final String USED_STATUS = "USED";//US
                public static final String CLOSED_STATUS = "CLOSED";
                public static final String CANCELED_STATUS = "CANCELLED";//RE
                public static final String UNDEFINED_STATUS = "UNDEFINED";
                 */
                if (pass(status)) {
                    return false;
                }

                //VERIFICA EN STATUS VP Y FD
                if (texto != null) {
                    for (int i = 0; i < texto.length(); i++) {
                        if ("0".indexOf(texto.charAt(i), 0) == -1) {
                            return false;
                        }
                    }
                }
                return true;
            }

            /**
             * Reviews the new cases entered and looks for the the associated referral to associate them
             * while finding new cases they are marked as associated
             */
            private void updateCasesAndReferrals() {
                logger.log(Level.INFO, "Updating cases and referrals");
                List<BusinessReferralCase> lastCasesFound = UpdateManager.getDefault("SYSTEM").getCurrentLastCases();
                //List<String> productNums = new ArrayList();
                boolean caseUpdated = false;
                boolean referralUpdated = false;
                for (BusinessReferralCase rCase : lastCasesFound) {
                    //Avoid repeating cases with same product number

                    if (!isEntyProductNumber(rCase.getProductNumber(), rCase.getStatus())) {//agregar que verifique el user?
                        //if (pass(rCase.getStatus()) || !productNums.contains(rCase.getProductNumber())) {
//                        if (pass(rCase.getStatus())) {

                        List<Referral> referralsRelatedToTheCase = new ArrayList();

                        if (rCase.getSource() != null && rCase.getSource().equals("CC")) {//It comes from credit scoring
                            //Find referrals by client id
                            referralsRelatedToTheCase = UpdateManager.getDefault("SYSTEM").getActiveReferralsByClientId(rCase);
                        } else {//It comes from Vision Plus or FDR
                            //Find referrals by productNumber
                            referralsRelatedToTheCase = UpdateManager.getDefault("SYSTEM").getActiveReferralsByProductNumber(rCase);
                        }

                        if (referralsRelatedToTheCase.size() > 0) {
                            //Update: associate the case to the referral by setting case associated referral id to the referral id
                            caseUpdated = UpdateManager.getDefault("SYSTEM").updateCaseToReferral(rCase, referralsRelatedToTheCase.get(0));

                            //Update: updates the referral to the case data. Thus updating the status entering customer info, product number etc.
                            try {
                                referralUpdated = UpdateManager.getDefault("SYSTEM").updateReferralToCase(rCase, referralsRelatedToTheCase.get(0));
                            } catch (Exception e) {
                                System.out.println("error");
                            }
                        }

                        //Let's enter the referral product num to make sure not workit again
                        // productNums.add(rCase.getProductNumber());

                        /* } else {
                        System.out.println("Case repeated!");
                        }*/
                    } else {
                        System.out.println("Case with of empty product number! (" + rCase.getClientFullName() + ")");
                    }
                }
                if (caseUpdated) {
                    //Let others know
                    pcs.firePropertyChange("CASES_UPDATED", null, 1);
                }

                if (referralUpdated) {
                    //Let others know
                    pcs.firePropertyChange("REFERRALS_UPDATED", null, 1);
                }
                logger.log(Level.INFO, "Cases and referrals updated!");
            }

            /**
             * Delete cases whose referral is no longer active
             * to keep things clean
             */
            private void deleteOldCases() {
                logger.log(Level.INFO, "Deleting old cases");
                UpdateManager.getDefault("SYSTEM").deleteOldCases();
                logger.log(Level.INFO, "Old cases deleted");
            }

            private void processReferralByStatus() {
                throw new UnsupportedOperationException("Not yet implemented");
            }

            private String translate(String statusFromCase) {

                if (statusFromCase.equalsIgnoreCase("EM")) {
                    return UpdateManager.ISSUED_STATUS;
                } else if (statusFromCase.equalsIgnoreCase("AC")) {
                    return UpdateManager.ACTIVATED_STATUS;
                } else if (statusFromCase.equalsIgnoreCase("US")) {
                    return UpdateManager.USED_STATUS;
                } else if (statusFromCase.equalsIgnoreCase("RE")) {
                    return UpdateManager.CANCELED_STATUS;
                } else if (statusFromCase.equalsIgnoreCase("CA")) {
                    return UpdateManager.CAPTURED_STATUS;
                } else if (statusFromCase.equalsIgnoreCase("AP")) {
                    return UpdateManager.APPROVED_STATUS;
                }else if (statusFromCase.equalsIgnoreCase("NC")) {
                    return UpdateManager.UNCONFIRMED;
                }
                
                return UpdateManager.NEW_STATUS;
            }

//            private boolean isOnlyNumbers(String textox) {
//                String numerosx = "0123456789";
//                for (int i = 0; i < textox.length(); i++) {
//                    if (numerosx.indexOf(textox.charAt(i), 0) == -1) {
//                        return false;
//                    }
//                }
//                return true;
//            }
            private String checkClientIdFormat(String idClient) {
                String result = null;
                if (idClient != null) {
                    result = idClient.replace("-", "");
                    return result;
                }

                return "";
            }

            private String checkProductNumberFormat(String idClient) {
                String result = null;
                if (idClient != null) {
                    result = idClient.replace("-", "");
                    return result;
                }

                return "-";
            }

            public String getDateStringFromDate(Date date, String format) {
                SimpleDateFormat formatoDeFecha = null;
                if (date != null) {
                    formatoDeFecha = new SimpleDateFormat(format);

                    return formatoDeFecha.format(date);
                }
                return null;
            }

            public Date getDateFromString(String date, String format) {
                Date fecha = null;
                try {
                    SimpleDateFormat formatoDelTexto = new SimpleDateFormat(format);
                    fecha = formatoDelTexto.parse(date);
                } catch (ParseException ex) {
                    System.out.println("Error");
                }
                return fecha;
            }

            private Date formatTimeStamp(String source) {
                Date temdate = null;
                if (source != null && !source.isEmpty()) {
                    String formatDateSource = "MM/dd/yyyy";
//                    String dateForMySQL = "yyyy-MM-dd HH:mm:ss";
                    temdate = getDateFromString(source, formatDateSource);
                    //String result = getDateStringFromDate(temdate, dateForMySQL);
                }
                return temdate;
            }

            private void moveCasesToDatabase(List<File> filesFound) throws IOException {
                logger.log(Level.INFO, "Moving cases found to db");
                CaseExtractor ce = new CaseExtractor();
                logger.log(Level.INFO, "-----VERIFCACION 1");
                BusinessReferralCaseJpaController controller = new BusinessReferralCaseJpaController();
                logger.log(Level.INFO, "-----VERIFCACION 2");
                if (filesFound.size() > 0) {
                    for (File file : filesFound) {
                        Map<Integer, Map<String, String>> aCase = ce.getCase(file);
//                        logger.log(Level.INFO, "-----VERIFCACION 3");
                        if (aCase.size() > 0) {
                            Set<Entry<Integer, Map<String, String>>> entrySet = aCase.entrySet();
                            BusinessReferralCase brc = new BusinessReferralCase();
//                            logger.log(Level.INFO, "-----VERIFCACION 4");
                            for (Entry<Integer, Map<String, String>> entry : entrySet) {
                                String caseSource = entry.getValue().get(UpdateManager.SOURCE);

                                //clean fields
                                brc.setAssociatedReferralId(0);
                                brc.setCif("");
                                brc.setClientAddress("");
                                brc.setClientFullName("");
                                brc.setClientId("");
                                brc.setClientPhones("");
                                brc.setId(0);
                                brc.setProductNumber("");
                                brc.setSource("");
                                brc.setStatus("");
                                brc.setTimeStamp(null);
                                brc.setWorkflowStatus("");

                                if (caseSource.equals("CC")) {
                                    brc.setCif(entry.getValue().get(UpdateManager.CIF));
                                    brc.setClientAddress("");
                                    brc.setClientFullName(entry.getValue().get(UpdateManager.CLIENT_FULL_NAME));
                                    brc.setClientId(checkClientIdFormat(entry.getValue().get(UpdateManager.CLIENT_ID)));


                                    String phones = entry.getValue().get(UpdateManager.TEL_RESIDENCIA);
                                    phones += "," + entry.getValue().get(UpdateManager.TEL_OFICINA);
                                    phones += "," + entry.getValue().get(UpdateManager.TEL_CELULAR);
                                    phones += "," + entry.getValue().get(UpdateManager.OTROS_TEL);
                                    brc.setClientPhones(phones);

                                    brc.setDocType(entry.getValue().get(UpdateManager.TIPO_DOC));
                                } else if (caseSource.equals("VP") || caseSource.equals("FD")) {
                                    brc.setCif("-");
                                    brc.setClientAddress("-");
                                    brc.setClientFullName("-");
                                    brc.setClientId("-");
                                    brc.setClientPhones("-");
                                    brc.setDocType("-");
                                }
                                brc.setId(0);//TODO warning
                                brc.setProductNumber(checkProductNumberFormat(entry.getValue().get(UpdateManager.CARD_NUMBER)));
                                //brc.setTimeStamp(new Date());
                                brc.setTimeStamp(formatTimeStamp(entry.getValue().get(UpdateManager.TIME_STAMP)));
//                                brc.setStatus(entry.getValue().get(UpdateManager.STATUS));
                                brc.setStatus(translate(entry.getValue().get(UpdateManager.STATUS)));
                                brc.setSource(caseSource);
                                brc.setAssociatedReferralId(0);
                                brc.setWorkflowStatus(UpdateManager.WF_STATUS_NEW);
                                if (brc.getId().intValue() > 0) {
                                    System.out.println("VALOR MAYOR QUE CERO PARA EL ID!");
                                }
//                                logger.log(Level.INFO, "-----VERIFCACION 5");
                                controller.create(brc);
//                                logger.log(Level.INFO, "-----VERIFCACION 6");
                            }
                        }
                    }
                }
                logger.log(Level.INFO, "New cases moved to db!");
            }

            /**
             * Just in case something has to be done with the cases before deleting
             */
            private void processUpdatedCases() {
                logger.log(Level.INFO, "Further processing referrals and cases.");
                //Nothing
            }
        };

        Thread t = new Thread(runnable);
        t.setName("MonitoringFiles");
        t.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread tt, Throwable e) {
                logger.log(Level.SEVERE, "Error running MonitoringFiles. Thrown inner exception is: {0}", e.getMessage());
                System.out.println("ESTADO ACTUAL IS:" + System.getProperty(NEWFILESMONITORBUSY));
                System.setProperty(NEWFILESMONITORBUSY, "0");
                System.out.println("ESTADO ACTUALIZADO IS:" + System.getProperty(NEWFILESMONITORBUSY));
//                NewFilesMonitor.RUNNING=false;
//                Thread tx = new Thread(runnable);
//                tx.setDaemon(true);
//                tx.start();
            }
        });
        t.setDaemon(true);
//        Installer.thread=t;
//         NewFilesMonitor.RUNNING=false;
        t.start();
    }

    public void addPropertyChange(PropertyChangeListener listener) {
        pcs.addPropertyChangeListener(listener);
    }

    public void removePropertyChangeListener(PropertyChangeListener listener) {
        pcs.removePropertyChangeListener(listener);
    }
}
